#define ZCORE_SOURCE
#include "BraceFile.hpp"
#include "StringTools.hpp"
#include "Log.hpp"

namespace zzz{

BraceItem::~BraceItem()
{
  Clear();
}

void BraceItem::Clear()
{
  for (vector<BraceItem*>::iterator vi=children_.begin();vi!=children_.end();vi++)
    delete *vi;
  children_.clear();
}

BraceItem* BraceItem::AddNode(const string &str, const char h, const char t)
{
  children_.push_back(new SmallBraceItem);
  children_.back()->text_=str;
  children_.back()->head_=h;
  children_.back()->tail_=t;
  return children_.back();
}

/////////////////////////////////////////////////////////////////////////////////
//BraceFile
void BraceFile::SetBraces(const string &braces)
{
  braces_=braces;
}

bool BraceFile::LoadFile(const string & filename)
{
  head_.Clear();
  stack<BraceItem*> parents;
  parents.push(&head_);
  BraceItem* parent=&head_;
  ifstream fi(filename);
  if (!fi.good())
  {
    ZLOG(ZERROR)<<"Cannot open file: "<<filename<<endl;
    return false;
  }

  bool inQuote=false;
  bool inComment=false;
  string str;
  while(true)
  {
    string line;
    getline(fi,line);
    if (fi.fail())
      break;

    zuint linelen=line.size();
    
    for (zuint i=0; i<linelen; i++)
    {
      char now=line[i];
      if (inComment && now!='*') continue;    //in block comment, ignore
      else if (inQuote && now!='\"')        //in quotation, add
      {
        str+=now;
        continue;
      }
      else if (now=='*')
      {
        if (i<linelen-1 && line[i+1]=='/')  //go out of block comment, eat next /
        {
          inComment=false;
          i++;
          continue;
        }
      }
      else if (now=='/')
      {
        if (i<linelen-1 && line[i+1]=='/')  //go in line comment, stop processing this line;
        {
          break;
        }
        if (i<linelen-1 && line[i+1]=='*')  //go in into block comment, eat the next '*'
        {
          i++;
          inComment=true;
          continue;
        }
      }
      else if (now=='\"')            //go in or out of quotation, add anyway
      {
        str+=now;
        inQuote=!inQuote;
        continue;
      }
      else
      {
        bool eaten=false;
        for (zuint j=0; j<braces_.size(); j+=2)
        {
          if (now==braces_[j])      //go into block children, finish current, and set new parent
          {
            str = Trim(str);
            if (!str.empty())
            {
              parent->AddNode(str);
              str.clear();
            }
            //Check Error
            else if (!parent->children_.empty() || parent->children_.back()->head_==0)
            {
              ZLOG(ZERROR)<<"No head for block!\n";
              break;
            }
            parents.push(parent->children_.back());  //always need to set the back as parent anyway
            parent=parents.top();
            parent->head_=braces_[j];
            parent->tail_=braces_[j+1];
            eaten=true;
            break;
          }
          else if (now==braces_[j+1])  //go out of block children, finish current, and set old parent
          {
            str = Trim(str);
            if (!str.empty())
            {
              parent->AddNode(str);
              str.clear();
            }
            parents.pop();
            parent=parents.top();
            eaten=true;
            break;
          }
        }
        if (eaten) continue;        //already processed, so go on next char
      }
      str+=now;                //not any special case, just add
    }
    //line finish
    if (inComment || inQuote) continue;
    //not in any special case, so finish current
    str = Trim(str);
    if (!str.empty())
    {
      parent->AddNode(str);
      str.clear();
    }
  }
  str = Trim(str);
  if (!str.empty())
  {
    parent->AddNode(str);
    str.clear();
  }
  fi.close();
  //Check Error
  if (parents.size()!=1)
  {
    ZLOG(ZERROR)<<"Unmatched brace in: "<<parent->text_<<" "<<parent->head_<<endl;
    return false;
  }
  if (inComment)
  {
    ZLOG(ZERROR)<<"Unmatched block command /*...*/\n";
    return false;
  }
  if (inQuote)
  {
    ZLOG(ZERROR)<<"Unmatched quotation marks \"...\"\n";
    return false;
  }

  return true;
}

bool BraceFile::SaveFile(const string &filename, const string &indent)
{
  ofstream fo(filename.c_str());
  if (!fo.good())
  {
    ZLOG(ZERROR)<<"Cannot open file: "<<filename<<endl;
    return false;
  }
  string head="";
  stack<BraceItem*> parents;
  stack<zuint> parents_idx;
  parents.push(&head_);
  BraceItem* parent=&head_;
  zuint i=0;
  while (true)
  {
    if (i==parent->children_.size())
    {
      if (parents.size()==1) break;  //finished
      else 
      {
        head.erase(head.size() - indent.size(), indent.size());
        fo<<head<<parent->tail_<<endl;
        parents.pop();
        parent=parents.top();
        i=parents_idx.top()+1;
        parents_idx.pop();
        continue;
      }
    }
    BraceItem* node=parent->children_[i];
    istringstream iss(node->text_);
    while(true)
    {
      string line;
      getline(iss,line);
      if (iss.fail()) break;
      if (node->head_==0)
        fo<<head<<line<<endl;
      else
        fo<<head<<line<<' '<<node->head_<<endl;
    }
    if (node->head_!=0)    //print children
    {
      parents.push(node);
      parent=parents.top();
      parents_idx.push(i);
      i=0;
      head+=indent;
      continue;
    }
    else 
      i++;
  }
  fo.close();
  return true;
}

BraceFile::BraceFile(const string &brace)
:BraceNode(&head_,NULL, 0)
{
  SetBraces(brace);
}

}
